from typing import List
import json
from fastapi import FastAPI, Request, Response
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.exceptions import RequestValidationError, ValidationError

from models import Category, Item, database


class MyException(Exception):
    def __init__(self, msg: any):
        self.msg = msg


import sys

from fastapi import APIRouter, Depends, FastAPI, Request
from loguru import logger
from starlette.routing import Match

logger.remove()
# logger.add(
#     sys.stdout,
#     colorize=True,
#     format="<green>{time:HH:mm:ss}</green> | {level} | <level>{message}</level>",
# )
# logger.add(
#     "sys.stdout",
#     colorize=True,
#     format="<green>{time:HH:mm:ss}</green> | {level} | <level>{message}</level>",
# )

router = APIRouter()


async def logging_dependency(request: Request):
    log_res = f"Params: {json.dumps(request.path_params)}"
    if request.method != "GET":
        try:
            res = await request.json()
        except Exception:
            res = await request.body()
            pass
        log_res += f", Body: {res}"
    logger.info(log_res)


app = FastAPI()
app.state.database = database


@app.middleware("http")
async def log_request(request, call_next):
    logger.info(f"{request.method} {request.url}")
    response = await call_next(request)
    body = b""
    async for chunk in response.body_iterator:
        body += chunk
    logger.info(f"Response Status code: {response.status_code}, body: {body.decode()}")

    # do something with body ...
    return Response(
        content=body,
        status_code=response.status_code,
        headers=dict(response.headers),
        media_type=response.media_type,
    )


@app.on_event("startup")
async def startup() -> None:
    database_ = app.state.database
    if not database_.is_connected:
        await database_.connect()


@app.on_event("shutdown")
async def shutdown() -> None:
    database_ = app.state.database
    if database_.is_connected:
        await database_.disconnect()


@app.exception_handler(MyException)
async def MyExceptionHandler(request: Request, exception: MyException):
    return JSONResponse(status_code=400, content={"msg": exception.msg})

@app.exception_handler(ValidationError)
async def MyExceptionHandler(request: Request, exception: ValidationError):
    return JSONResponse(status_code=400, content={"msg": exception.errors()})


@router.get("/items/", response_model=List[Item])
async def get_items():
    items = await Item.objects.select_related("category").all()
    return items


@router.post("/items/", response_model=Item)
async def create_item(item: Item):
    if not await Category.objects.filter(id=item.category).exists():
        # print('item.category',item.category.id)
        # print('item.category',dir(item))
        raise MyException({"category": str(item.category.id) + " category not exist."})
    logger.info(f'===== item {item.name}',)
    await item.save()
    logger.info(f'===== item {item.dict()}',)

    return item


@router.post("/categories/", response_model=Category)
async def create_category(category: Category):
    await category.save()
    return category

@router.get("/categories/", response_model=List[Category])
async def list_category():
    items = await Category.objects.select_related('items').all()
    return items


@router.put("/items/{item_id}")
async def get_item(item_id: int, item: Item):
    item_db = await Item.objects.get(pk=item_id)
    return await item_db.update(**item.dict())


@router.delete("/items/{item_id}")
async def delete_item(item_id: int, item: Item = None):
    if item:
        return {"deleted_rows": await item.delete()}
    item_db = await Item.objects.get(pk=item_id)
    return {"deleted_rows": await item_db.delete()}


###########


@router.get("/")
async def root():
    return {"message": "Hello World"}


app.mount("/static", StaticFiles(directory="static"), name="static")


templates = Jinja2Templates(directory="templates")


@router.get("/echart", response_class=HTMLResponse)
async def index(request: Request):
    return templates.TemplateResponse("echart.html", {"request": request, "id": id})


app.include_router(router, dependencies=[Depends(logging_dependency)])
